/*
 * Copyright (c) Kumo Inc. and affiliates.
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>

#include <melon/cportability.h>
#include <melon/portability.h>
#include <melon/traits.h>

namespace melon {
    /*
     * MELON_DECLVAL(T)
     *
     * This macro works like std::declval<T>() but does the same thing in a way
     * that does not require instantiating a function template.
     *
     * Use this macro instead of std::declval<T>() in places that are widely
     * instantiated to reduce compile-time overhead of instantiating function
     * templates.
     *
     * Note that, like std::declval<T>(), this macro can only be used in
     * unevaluated contexts.
     *
     * There are some small differences between this macro and std::declval<T>().
     * - This macro results in a value of type 'T' instead of 'T&&'.
     * - This macro requires the type T to be a complete type at the
     *   point of use.
     *   If this is a problem then use MELON_DECLVAL(T&&) instead, or if T might
     *   be 'void', then use MELON_DECLVAL(std::add_rvalue_reference_t<T>).
     */
#define MELON_DECLVAL(...) static_cast<__VA_ARGS__ (*)() noexcept>(nullptr)()

    namespace detail {
        template<typename T>
        T decay_1_(T const volatile &&);

        template<typename T>
        T decay_1_(T const &);

        template<typename T>
        T *decay_1_(T *);

        template<typename T>
        auto decay_0_(int) -> decltype(detail::decay_1_(MELON_DECLVAL(T&&)));

        template<typename T>
        auto decay_0_(short) -> void;

        template<typename T>
        using decay_t = decltype(detail::decay_0_<T>(0));
    } // namespace detail

    //  decay_t
    //
    //  Like std::decay_t but possibly faster to compile.
    //
    //  mimic: std::decay_t, C++14
    using detail::decay_t;

    /**
     *  copy
     *
     *  Usable when you have a function with two overloads:
     *
     *      class MyData;
     *      void something(MyData&&);
     *      void something(const MyData&);
     *
     *  Where the purpose is to make copies and moves explicit without having to
     *  spell out the full type names - in this case, for copies, to invoke copy
     *  constructors.
     *
     *  When the caller wants to pass a copy of an lvalue, the caller may:
     *
     *      void foo() {
     *        MyData data;
     *        something(melon::copy(data)); // explicit copy
     *        something(std::move(data)); // explicit move
     *        something(data); // const& - neither move nor copy
     *      }
     *
     *  Note: If passed an rvalue, invokes the move-ctor, not the copy-ctor. This
     *  can be used to to force a move, where just using std::move would not:
     *
     *      melon::copy(std::move(data)); // force-move, not just a cast to &&
     *
     *  Note: The following text appears in the standard:
     *
     *      In several places in this Clause the operation //DECAY_COPY(x)// is
     *      used. All such uses mean call the function `decay_copy(x)` and use the
     *      result, where `decay_copy` is defined as follows:
     *
     *        template <class T> decay_t<T> decay_copy(T&& v)
     *          { return std::forward<T>(v); }
     *
     *      http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
     *        30.2.6 `decay_copy` [thread.decaycopy].
     *
     *  We mimic it, with a `noexcept` specifier for good measure.
     */

    template<typename T>
    constexpr detail::decay_t<T> copy(T &&value) noexcept(
        noexcept(detail::decay_t<T>(static_cast<T &&>(value)))) {
        return static_cast<T &&>(value);
    }

    //  mimic: forward_like, p0847r0
    template<typename Src, typename Dst>
    constexpr like_t<Src, Dst> &&forward_like(Dst &&dst) noexcept {
        return std::forward<like_t<Src, Dst> >(static_cast<Dst &&>(dst));
    }

    /**
     * Initializer lists are a powerful compile time syntax introduced in C++11
     * but due to their often conflicting syntax they are not used by APIs for
     * construction.
     *
     * Further standard conforming compilers *strongly* favor an
     * std::initializer_list overload for construction if one exists.  The
     * following is a simple tag used to disambiguate construction with
     * initializer lists and regular uniform initialization.
     *
     * For example consider the following case
     *
     *  class Something {
     *  public:
     *    explicit Something(int);
     *    Something(std::initializer_list<int>);
     *
     *    operator int();
     *  };
     *
     *  ...
     *  Something something{1}; // SURPRISE!!
     *
     * The last call to instantiate the Something object will go to the
     * initializer_list overload.  Which may be surprising to users.
     *
     * If however this tag was used to disambiguate such construction it would be
     * easy for users to see which construction overload their code was referring
     * to.  For example
     *
     *  class Something {
     *  public:
     *    explicit Something(int);
     *    Something(melon::initlist_construct_t, std::initializer_list<int>);
     *
     *    operator int();
     *  };
     *
     *  ...
     *  Something something_one{1}; // not the initializer_list overload
     *  Something something_two{melon::initlist_construct, {1}}; // correct
     */
    struct initlist_construct_t {
    };

    constexpr initlist_construct_t initlist_construct{};

    //  sorted_unique_t, sorted_unique
    //
    //  A generic tag type and value to indicate that some constructor or method
    //  accepts a container in which the values are sorted and unique.
    //
    //  Example:
    //
    //    void takes_numbers(melon::sorted_unique_t, std::vector<int> alist) {
    //      assert(std::is_sorted(alist.begin(), alist.end()));
    //      assert(std::unique(alist.begin(), alist.end()) == alist.end());
    //      for (i : alist) {
    //        // some behavior which safe only when alist is sorted and unique
    //      }
    //    }
    //    void takes_numbers(std::vector<int> alist) {
    //      std::sort(alist.begin(), alist.end());
    //      alist.erase(std::unique(alist.begin(), alist.end()), alist.end());
    //      takes_numbers(melon::sorted_unique, alist);
    //    }
    //
    //  mimic: std::sorted_unique_t, std::sorted_unique, p0429r6
    struct sorted_unique_t {
    };

    constexpr sorted_unique_t sorted_unique{};

    //  sorted_equivalent_t, sorted_equivalent
    //
    //  A generic tag type and value to indicate that some constructor or method
    //  accepts a container in which the values are sorted but not necessarily
    //  unique.
    //
    //  Example:
    //
    //    void takes_numbers(melon::sorted_equivalent_t, std::vector<int> alist) {
    //      assert(std::is_sorted(alist.begin(), alist.end()));
    //      for (i : alist) {
    //        // some behavior which safe only when alist is sorted
    //      }
    //    }
    //    void takes_numbers(std::vector<int> alist) {
    //      std::sort(alist.begin(), alist.end());
    //      takes_numbers(melon::sorted_equivalent, alist);
    //    }
    //
    //  mimic: std::sorted_equivalent_t, std::sorted_equivalent, p0429r6
    struct sorted_equivalent_t {
    };

    constexpr sorted_equivalent_t sorted_equivalent{};

    template<typename T>
    struct transparent : T {
        using is_transparent = void;
        using T::T;
    };

    /**
     * A simple function object that passes its argument through unchanged.
     *
     * Example:
     *
     *   int i = 42;
     *   int &j = identity(i);
     *   assert(&i == &j);
     *
     * Warning: passing a prvalue through identity turns it into an xvalue,
     * which can effect whether lifetime extension occurs or not. For instance:
     *
     *   auto&& x = std::make_unique<int>(42);
     *   cout << *x ; // OK, x refers to a valid unique_ptr.
     *
     *   auto&& y = identity(std::make_unique<int>(42));
     *   cout << *y ; // ERROR: y did not lifetime-extend the unique_ptr. It
     *                // is no longer valid
     */
    struct identity_fn {
        template<class T>
        constexpr T &&operator()(T &&x) const noexcept {
            return static_cast<T &&>(x);
        }
    };

    using Identity = identity_fn;
    inline constexpr identity_fn identity{};

    /// literal_string
    ///
    /// A structural type representing a literal string. A structural type may be
    /// a non-type template argument.
    ///
    /// May at times be useful since language-level literal strings are not allowed
    /// as non-type template arguments.
    ///
    /// This may typically be used with vtag for passing the literal string as a
    /// constant-expression via a non-type template argument.
    ///
    /// Example:
    ///
    ///   template <size_t N, literal_string<char, N> Str>
    ///   void do_something_with_literal_string(vtag_t<Str>);
    ///
    ///   void do_something() {
    ///     do_something_with_literal_string(vtag<literal_string{"foobar"}>);
    ///   }
    template<typename C, std::size_t N>
    struct literal_string {
        C buffer[N];

        MELON_CONSTEVAL /* implicit */ literal_string(C const (&buf)[N]) noexcept {
            for (std::size_t i = 0; i < N; ++i) {
                buffer[i] = buf[i];
            }
        }

        constexpr std::size_t size() const noexcept { return N - 1; }
        constexpr C const *data() const noexcept { return buffer; }
        constexpr C const *c_str() const noexcept { return buffer; }

        template<
            typename String,
            decltype((void(String(MELON_DECLVAL(C const*), N - 1)), 0)) = 0>
        constexpr explicit operator String() const //
            noexcept(noexcept(String(MELON_DECLVAL(C const*), N - 1))) {
            return String(data(), N - 1);
        }
    };

    inline namespace literals {
        inline namespace string_literals {
#if MELON_CPLUSPLUS >= 202002 && !defined(__NVCC__)
template <literal_string Str>
MELON_CONSTEVAL decltype(Str) operator""_lit() noexcept {
  return Str;
}
template <literal_string Str>
MELON_CONSTEVAL vtag_t<Str> operator""_litv() noexcept {
  return vtag<Str>;
}
#endif
        } // namespace string_literals
    } // namespace literals

    namespace detail {
        template<typename T>
        struct inheritable_inherit_ : T {
            using T::T;

            template<
                typename... A,
                std::enable_if_t<std::is_constructible<T, A...>::value, int>  = 0>
            /* implicit */ MELON_ERASE inheritable_inherit_(A &&... a) noexcept(
                noexcept(T(static_cast<A &&>(a)...)))
                : T(static_cast<A &&>(a)...) {
            }
        };

        template<typename T>
        struct inheritable_contain_ {
            T v;

            template<
                typename... A,
                std::enable_if_t<std::is_constructible<T, A...>::value, int>  = 0>
            /* implicit */ MELON_ERASE inheritable_contain_(A &&... a) noexcept(
                noexcept(T(static_cast<A &&>(a)...)))
                : v(static_cast<A &&>(a)...) {
            }

            MELON_ERASE operator T &() & noexcept { return v; }
            MELON_ERASE operator T &&() && noexcept { return static_cast<T &&>(v); }
            MELON_ERASE operator T const &() const & noexcept { return v; }
            MELON_ERASE operator T const &&() const && noexcept {
                return static_cast<T const &&>(v);
            }
        };

        template<bool>
        struct inheritable_;

        template<>
        struct inheritable_<false> {
            template<typename T>
            using apply = inheritable_inherit_<T>;
        };

        template<>
        struct inheritable_<true> {
            template<typename T>
            using apply = inheritable_contain_<T>;
        };

        //  inheritable
        //
        //  A class wrapping an arbitrary type T which is always inheritable, and which
        //  enables empty-base-optimization when possible.
        template<typename T>
        using inheritable =
        typename inheritable_<std::is_final<T>::value>::template apply<T>;
    } // namespace detail

    // Prevent child classes from finding anything in melon:: by ADL.
    namespace moveonly_ {
        /**
         * Disallow copy but not move in derived types. This is essentially
         * boost::noncopyable (the implementation is almost identical), except:
         * 1) It doesn't delete move constructor and move assignment.
         * 2) It has public methods, enabling aggregate initialization.
         */
        struct MoveOnly {
            constexpr MoveOnly() noexcept = default;

            ~MoveOnly() noexcept = default;

            MoveOnly(MoveOnly &&) noexcept = default;

            MoveOnly &operator=(MoveOnly &&) noexcept = default;

            MoveOnly(const MoveOnly &) = delete;

            MoveOnly &operator=(const MoveOnly &) = delete;
        };

        /**
         * Disallow copy and move for derived types. This is essentially
         * boost::noncopyable (the implementation is almost identical), except it has
         * public methods, enabling aggregate initialization.
         */
        struct NonCopyableNonMovable {
            constexpr NonCopyableNonMovable() noexcept = default;

            ~NonCopyableNonMovable() noexcept = default;

            NonCopyableNonMovable(NonCopyableNonMovable &&) = delete;

            NonCopyableNonMovable &operator=(NonCopyableNonMovable &&) = delete;

            NonCopyableNonMovable(const NonCopyableNonMovable &) = delete;

            NonCopyableNonMovable &operator=(const NonCopyableNonMovable &) = delete;
        };

        struct Default {
        };

        template<bool Copy, bool Move>
        using EnableCopyMove = std::conditional_t<
            Copy,
            Default,
            std::conditional_t<Move, MoveOnly, NonCopyableNonMovable> >;
    } // namespace moveonly_

    using moveonly_::MoveOnly;
    using moveonly_::NonCopyableNonMovable;

    /// variadic_noop
    /// variadic_noop_fn
    ///
    /// An invocable object and type that has no side-effects - that does nothing
    /// when invoked regardless of the arguments with which it is invoked - and that
    /// returns void.
    ///
    /// May be invoked with any arguments. Returns void.
    struct variadic_noop_fn {
        template<typename... A>
        constexpr void operator()(A &&...) const noexcept {
        }
    };

    inline constexpr variadic_noop_fn variadic_noop;

    /// variadic_constant_of
    /// variadic_constant_of_fn
    ///
    /// An invocable object and type that has no side-effects - that does nothing
    /// when invoked regardless of the arguments with which it is invoked - and that
    /// returns a constant value.
    template<auto Value>
    struct variadic_constant_of_fn {
        using value_type = decltype(Value);
        static inline constexpr value_type value = Value;

        template<typename... A>
        constexpr value_type operator()(A &&...) const noexcept {
            return value;
        }
    };

    template<auto Value>
    inline constexpr variadic_constant_of_fn<Value> variadic_constant_of;

    //  unsafe_default_initialized
    //  unsafe_default_initialized_cv
    //
    //  An object which is explicitly convertible to any default-constructible type
    //  and which, upon conversion, yields a default-initialized value of that type.
    //
    //  https://en.cppreference.com/w/cpp/language/default_initialization
    //
    //  For fundamental types, a default-initialized instance may have indeterminate
    //  value. Reading an indeterminate value is undefined behavior but may offer a
    //  performance optimization. When using an indeterminate value as a performance
    //  optimization, it is best to be explicit.
    //
    //  Useful as an escape hatch when enabling warnings or errors:
    //  * gcc:
    //    * uninitialized
    //    * maybe-uninitialized
    //  * clang:
    //    * uninitialized
    //    * conditional-uninitialized
    //    * sometimes-uninitialized
    //    * uninitialized-const-reference
    //  * msvc:
    //    * C4701: potentially uninitialized local variable used
    //    * C4703: potentially uninitialized local pointer variable used
    //
    //  Example:
    //
    //      int local = melon::unsafe_default_initialized;
    //      store_value_into_int_ptr(&value); // suppresses possible warning
    //      use_value(value); // suppresses possible warning
    struct unsafe_default_initialized_cv {
        MELON_PUSH_WARNING
        // MSVC requires warning disables to be outside of function definition
        // Uninitialized local variable 'uninit' used
        MELON_MSVC_DISABLE_WARNING(4700)
        // Potentially uninitialized local variable 'uninit' used
        MELON_MSVC_DISABLE_WARNING(4701)
        // Potentially uninitialized local pointer variable 'uninit' used
        MELON_MSVC_DISABLE_WARNING(4703)
        MELON_GNU_DISABLE_WARNING("-Wuninitialized")
        // Clang doesn't implement -Wmaybe-uninitialized and warns about it
        MELON_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")

        template<typename T>
        MELON_ERASE constexpr /* implicit */ operator T() const noexcept {
#if defined(__cpp_lib_is_constant_evaluated)
#if __cpp_lib_is_constant_evaluated >= 201811L
#if (defined(_MSC_VER) && !defined(__MSVC_RUNTIME_CHECKS)) || \
    (defined(__clang__) && !defined(__GNUC__))
    if (!std::is_constant_evaluated()) {
      T uninit;
      return uninit;
    }
#endif
#endif
#endif
            return T();
        }

        MELON_POP_WARNING
    };

    inline constexpr unsafe_default_initialized_cv unsafe_default_initialized{};

    /// to_bool
    /// to_bool_fn
    ///
    /// Constructs a boolean from the argument.
    ///
    /// Particularly useful for testing sometimes-weak function declarations. They
    /// may be declared weak on some platforms but not on others. GCC likes to warn
    /// about them but the warning is unhelpful.
    struct to_bool_fn {
        template<typename..., typename T>
        MELON_ERASE constexpr auto operator()(T const &t) const noexcept
            -> decltype(static_cast<bool>(t)) {
            MELON_PUSH_WARNING
            MELON_GCC_DISABLE_WARNING("-Waddress")
            MELON_GCC_DISABLE_WARNING("-Wnonnull-compare")
            return static_cast<bool>(t);
            MELON_POP_WARNING
        }
    };

    inline constexpr to_bool_fn to_bool{};

    struct to_signed_fn {
        template<typename..., typename T>
        constexpr auto operator()(T const &t) const noexcept ->
            typename std::make_signed<T>::type {
            using S = typename std::make_signed<T>::type;
            // note: static_cast<S>(t) would be more straightforward, but it would also
            // be implementation-defined behavior and that is typically to be avoided;
            // the following code optimized into the same thing, though
            constexpr auto m = static_cast<T>(std::numeric_limits<S>::max());
            return m < t ? -static_cast<S>(~t) + S{-1} : static_cast<S>(t);
        }
    };

    inline constexpr to_signed_fn to_signed{};

    struct to_unsigned_fn {
        template<typename..., typename T>
        constexpr auto operator()(T const &t) const noexcept ->
            typename std::make_unsigned<T>::type {
            using U = typename std::make_unsigned<T>::type;
            return static_cast<U>(t);
        }
    };

    inline constexpr to_unsigned_fn to_unsigned{};

    namespace detail {
        template<typename Src, typename Dst>
        inline constexpr bool is_to_narrow_convertible_v =
                (std::is_integral<Dst>::value) &&
                (std::is_signed<Dst>::value == std::is_signed<Src>::value);
    }

    template<typename Src>
    class to_narrow_convertible {
        static_assert(std::is_integral<Src>::value, "not an integer");

        template<typename Dst>
        struct to_
                : std::bool_constant<detail::is_to_narrow_convertible_v<Src, Dst> > {
        };

    public:
        explicit constexpr to_narrow_convertible(Src const &value) noexcept
            : value_(value) {
        }

        explicit to_narrow_convertible(to_narrow_convertible const &) = default;

        explicit to_narrow_convertible(to_narrow_convertible &&) = default;

        to_narrow_convertible &operator=(to_narrow_convertible const &) = default;

        to_narrow_convertible &operator=(to_narrow_convertible &&) = default;

        template<typename Dst, std::enable_if_t<to_<Dst>::value, int>  = 0>
        /* implicit */ constexpr operator Dst() const noexcept {
            MELON_PUSH_WARNING
            MELON_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments
            MELON_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables
            MELON_GNU_DISABLE_WARNING("-Wconversion")
            return value_;
            MELON_POP_WARNING
        }

    private:
        Src value_;
    };

    //  to_narrow
    //
    //  A utility for performing explicit possibly-narrowing integral conversion
    //  without specifying the destination type. Does not permit changing signs.
    //  Sometimes preferable to static_cast<Dst>(src) to document the intended
    //  semantics of the cast.
    //
    //  Models explicit conversion with an elided destination type. Sits in between
    //  a stricter explicit conversion with a named destination type and a more
    //  lenient implicit conversion. Implemented with implicit conversion in order
    //  to take advantage of the undefined-behavior sanitizer's inspection of all
    //  implicit conversions - it checks for truncation, with suppressions in place
    //  for warnings which guard against narrowing implicit conversions.
    struct to_narrow_fn {
        template<typename..., typename Src>
        constexpr auto operator()(Src const &src) const noexcept
            -> to_narrow_convertible<Src> {
            return to_narrow_convertible<Src>{src};
        }
    };

    inline constexpr to_narrow_fn to_narrow{};

    template<typename Src>
    class to_integral_convertible {
        static_assert(std::is_floating_point<Src>::value, "not a floating-point");

        template<typename Dst>
        static constexpr bool to_ = std::is_integral<Dst>::value;

    public:
        explicit constexpr to_integral_convertible(Src const &value) noexcept
            : value_(value) {
        }

        explicit to_integral_convertible(to_integral_convertible const &) = default;

        explicit to_integral_convertible(to_integral_convertible &&) = default;

        to_integral_convertible &operator=(to_integral_convertible const &) = default;

        to_integral_convertible &operator=(to_integral_convertible &&) = default;

        template<typename Dst, std::enable_if_t<to_<Dst>, int>  = 0>
        /* implicit */ constexpr operator Dst() const noexcept {
            MELON_PUSH_WARNING
            MELON_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments
            MELON_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables
            MELON_GNU_DISABLE_WARNING("-Wconversion")
            return value_;
            MELON_POP_WARNING
        }

    private:
        Src value_;
    };

    //  to_integral
    //
    //  A utility for performing explicit floating-point-to-integral conversion
    //  without specifying the destination type. Sometimes preferable to
    //  static_cast<Dst>(src) to document the intended semantics of the cast.
    //
    //  Models explicit conversion with an elided destination type. Sits in between
    //  a stricter explicit conversion with a named destination type and a more
    //  lenient implicit conversion. Implemented with implicit conversion in order
    //  to take advantage of the undefined-behavior sanitizer's inspection of all
    //  implicit conversions.
    struct to_integral_fn {
        template<typename..., typename Src>
        constexpr auto operator()(Src const &src) const noexcept
            -> to_integral_convertible<Src> {
            return to_integral_convertible<Src>{src};
        }
    };

    inline constexpr to_integral_fn to_integral{};

    template<typename Src>
    class to_floating_point_convertible {
        static_assert(std::is_integral<Src>::value, "not a floating-point");

        template<typename Dst>
        static constexpr bool to_ = std::is_floating_point<Dst>::value;

    public:
        explicit constexpr to_floating_point_convertible(Src const &value) noexcept
            : value_(value) {
        }

        explicit to_floating_point_convertible(to_floating_point_convertible const &) = default;

        explicit to_floating_point_convertible(to_floating_point_convertible &&) = default;

        to_floating_point_convertible &operator=(
            to_floating_point_convertible const &) = default;

        to_floating_point_convertible &operator=(to_floating_point_convertible &&) = default;

        template<typename Dst, std::enable_if_t<to_<Dst>, int>  = 0>
        /* implicit */ constexpr operator Dst() const noexcept {
            MELON_PUSH_WARNING
            MELON_GNU_DISABLE_WARNING("-Wconversion")
            return value_;
            MELON_POP_WARNING
        }

    private:
        Src value_;
    };

    //  to_floating_point
    //
    //  A utility for performing explicit integral-to-floating-point conversion
    //  without specifying the destination type. Sometimes preferable to
    //  static_cast<Dst>(src) to document the intended semantics of the cast.
    //
    //  Models explicit conversion with an elided destination type. Sits in between
    //  a stricter explicit conversion with a named destination type and a more
    //  lenient implicit conversion. Implemented with implicit conversion in order
    //  to take advantage of the undefined-behavior sanitizer's inspection of all
    //  implicit conversions.
    struct to_floating_point_fn {
        template<typename..., typename Src>
        constexpr auto operator()(Src const &src) const noexcept
            -> to_floating_point_convertible<Src> {
            return to_floating_point_convertible<Src>{src};
        }
    };

    inline constexpr to_floating_point_fn to_floating_point{};

    struct to_underlying_fn {
        template<typename..., class E>
        constexpr std::underlying_type_t<E> operator()(E e) const noexcept {
            static_assert(std::is_enum<E>::value, "not an enum type");
            return static_cast<std::underlying_type_t<E>>(e);
        }
    };

    inline constexpr to_underlying_fn to_underlying{};

    namespace detail {
        template<typename R>
        using invocable_to_detect = decltype(MELON_DECLVAL(R)());

        template<
            typename F,
            //  MSVC 14.16.27023 does not permit these to be in the class body:
            //    error C2833: 'operator decltype' is not a recognized operator or type
            //  TODO: return these to the class body and remove the static assertions
            typename TML = detected_t<invocable_to_detect, F &>,
            typename TCL = detected_t<invocable_to_detect, F const &>,
            typename TMR = detected_t<invocable_to_detect, F &&>,
            typename TCR = detected_t<invocable_to_detect, F const &&> >
        class invocable_to_convertible : private inheritable<F> {
        private:
            static_assert(std::is_same<F, decay_t<F> >::value, "mismatch");

            template<typename R>
            using result_t = detected_t<invocable_to_detect, R>;
            template<typename R>
            static constexpr bool detected_v = is_detected_v<invocable_to_detect, R>;
            template<typename R>
            using if_invocable_as_v = std::enable_if_t<detected_v<R>, int>;
            template<typename R>
            static constexpr bool nx_v = noexcept(MELON_DECLVAL(R)());
            template<typename G>
            static constexpr bool constructible_v = std::is_constructible<F, G &&>::value;

            using FML = F &;
            using FCL = F const &;
            using FMR = F &&;
            using FCR = F const &&;
            static_assert(std::is_same<TML, result_t<FML> >::value, "mismatch");
            static_assert(std::is_same<TCL, result_t<FCL> >::value, "mismatch");
            static_assert(std::is_same<TMR, result_t<FMR> >::value, "mismatch");
            static_assert(std::is_same<TCR, result_t<FCR> >::value, "mismatch");

        public:
            template<typename G, std::enable_if_t<constructible_v<G &&>, int>  = 0>
            MELON_ERASE explicit constexpr invocable_to_convertible(G &&g) noexcept(
                noexcept(F(static_cast<G &&>(g))))
                : inheritable<F>(static_cast<G &&>(g)) {
            }

            template<typename..., typename R = FML, if_invocable_as_v<R>  = 0>
            MELON_ERASE constexpr operator TML() & noexcept(nx_v<R>) {
                return static_cast<FML>(*this)();
            }

            template<typename..., typename R = FCL, if_invocable_as_v<R>  = 0>
            MELON_ERASE constexpr operator TCL() const & noexcept(nx_v<R>) {
                return static_cast<FCL>(*this)();
            }

            template<typename..., typename R = FMR, if_invocable_as_v<R>  = 0>
            MELON_ERASE constexpr operator TMR() && noexcept(nx_v<R>) {
                return static_cast<FMR>(*this)();
            }

            template<typename..., typename R = FCR, if_invocable_as_v<R>  = 0>
            MELON_ERASE constexpr operator TCR() const && noexcept(nx_v<R>) {
                return static_cast<FCR>(*this)();
            }
        };
    } // namespace detail

    //  invocable_to
    //  invocable_to_fn
    //
    //  Given an invocable, returns an object which is implicitly convertible to the
    //  type which the invocable returns when invoked with no arguments. Conversion
    //  invokes the invocables and returns the value.
    //
    //  The return object has unspecified type with the following semantics:
    //  * It stores a decay-copy of the passed invocable.
    //  * It defines four-way conversion operators. Each conversion operator purely
    //    forwards to the invocable as forwarded-like the convertible, and has the
    //    same exception specification and the same participation in overload
    //    resolution as invocation of the invocable.
    //
    //  Example:
    //
    //    Given a setup:
    //
    //      struct stable {
    //        int value = 0;
    //        stable() = default;
    //        stable(stable const&); // expensive!
    //      };
    //      std::list<stable const> list;
    //
    //    The goal is to insert a stable with a value of 7 to the back of the list.
    //
    //    The obvious ways are expensive:
    //
    //      stable obj;
    //      obj.value = 7;
    //      list.push_back(obj); // or variations with emplace_back or std::move
    //
    //    With a lambda and copy elision optimization (NRVO), the expense remains:
    //
    //      list.push_back(std::invoke([] {
    //        stable obj;
    //        obj.value = 7;
    //        return obj;
    //      }));
    //
    //    But conversion, as done with this utility, makes this goal achievable.
    //
    //      list.emplace_back(melon::invocable_to([] {
    //        stable obj;
    //        obj.value = 7;
    //        return obj;
    //      }));
    struct invocable_to_fn {
        template<
            typename F,
            typename...,
            typename D = detail::decay_t<F>,
            typename R = detail::invocable_to_convertible<D>,
            std::enable_if_t<std::is_constructible<D, F &&>::value, int>  = 0>
        MELON_ERASE constexpr R operator()(F &&f) const
            noexcept(noexcept(R(static_cast<F &&>(f)))) {
            return R(static_cast<F &&>(f));
        }
    };

    inline constexpr invocable_to_fn invocable_to{};
} // namespace melon
